/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.wuhenzhizao.titlebar.widget;

import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.Text;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.Rect;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.multimodalinput.event.TouchEvent;

/**
 * RippleView
 *
 * @since 2021-06-08
 */
public class RippleView extends Text implements Component.DrawTask, Component.TouchEventListener {
    private static final int DRAW_RADIUS_ZERO = 0;
    private static final double DRAW_RADIUS_F = 0.5f;
    private static final int DRAW_RADIUS_MOVE = 50;
    private static final int DURATION = 50;
    private static final int FREQUENCY = 10;
    private float mAlphaFactor = 0.2f;
    private int mDownX;
    private int mDownY;
    private double mDensity;
    private double mDrawRadius;
    private double mMaxRadius;
    private double mStepRadius;
    private boolean isDrawFinish;
    private double mCycle;
    private final Rect mRect = new Rect();
    private boolean isPressUp = false;
    private Paint mRevealPaint;
    private int mRippleColor = Color.BLACK.getValue();
    private boolean isHover = true;
    private AnimatorValue mRadiusAnimator;
    private int mDuration = 6000;
    private boolean isPointMove = false;

    private int mStatusBarHeight;

    /**
     * RippleView
     *
     * @param context context
     * @param attrSet attrSet
     */
    public RippleView(Context context, AttrSet attrSet) {
        this(context, attrSet, "");
    }

    /**
     * RippleView
     *
     * @param context context
     * @param attrSet attrSet
     * @param styleName styleName
     */
    public RippleView(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        mRippleColor = Utils.getColorFromAttr(attrSet, "rippleColor", mRippleColor);
        mAlphaFactor = Utils.getFloatFromAttr(attrSet, "alphaFactor", mAlphaFactor);
        isHover = Utils.getBooleanFromAttr(attrSet, "hover", isHover);
        if (isHover) {
            initView();
            startAnimation();
            setTouchEventListener(this);
            addDrawTask(this);
            setBindStateChangedListener(new BindStateChangedListener() {
                @Override
                public void onComponentBoundToWindow(Component component) {
                    mRect.set(getLeft(), getTop(), getRight(), getBottom());
                }

                @Override
                public void onComponentUnboundFromWindow(Component component) {
                }
            });
        }
    }

    @Override
    public void addDrawTask(DrawTask task) {
        super.addDrawTask(task);
        task.onDraw(this, mCanvasForTaskOverContent);
    }

    /**
     * initView
     */
    public void initView() {
        mCycle = DURATION / FREQUENCY;
        mDensity = DisplayManager.getInstance().getDefaultDisplay(getContext()).get().getAttributes().densityPixels;
        mCycle = mDensity * mCycle;
        isDrawFinish = true;

        mRevealPaint = new Paint();
        mRevealPaint.setAntiAlias(true);
        mRevealPaint.setColor(new Color(adjustAlpha(mRippleColor, mAlphaFactor)));

        int statusBarHeight = 0;
        try {
            String invoke = Utils.invokeStringValue("ro.config.hw_notch_size", "288,129,0,0");
            if (invoke != null) {
                String splitString = invoke.split(",")[1];
                if (splitString != null) {
                    statusBarHeight = Integer.parseInt(splitString);
                }
            }
        } catch (NumberFormatException e) {
            Utils.debug("NumberFormatException " + e.toString());
        }
        this.mStatusBarHeight = statusBarHeight;
        Utils.debug("mStatusBarHeight = " + this.mStatusBarHeight);
    }

    private void startAnimation() {
        mRadiusAnimator = new AnimatorValue();
        mRadiusAnimator.setDuration(mDuration);
        mRadiusAnimator.setLoopedCount(AnimatorValue.INFINITE);
        mRadiusAnimator.setCurveType(AnimatorValue.CurveType.ACCELERATE_DECELERATE);
        mRadiusAnimator.setValueUpdateListener((animatorValue, v) -> {
            invalidate();
        });
        mRadiusAnimator.start();
    }

    private void updateDrawData() {
        setDrawRadius(DRAW_RADIUS_ZERO);
        int radiusLeftTop = (int) Math.sqrt(mDownX * mDownX + mDownY * mDownY);
        int radiusRightTop = (int) Math.sqrt((mRect.right - (mDownX + getLeft())) * (mRect.right - (mDownX + getLeft()))
                + mDownY * mDownY);
        int radiusLeftBottom = (int) Math.sqrt(mDownX * mDownX
                + (mRect.getHeight() - mDownY) * (mRect.getHeight() - mDownY));
        int radiusRightBottom = (int) Math.sqrt((mRect.right - (mDownX + getLeft())) * (mRect.right
                - (mDownX + getLeft())) + (mRect.getHeight() - mDownY) * (mRect.getHeight() - mDownY));
        mMaxRadius = Utils.getMax(radiusLeftTop, radiusRightTop, radiusLeftBottom, radiusRightBottom);

        mStepRadius = mMaxRadius / mCycle;
    }

    /**
     * adjustAlpha
     *
     * @param color color
     * @param factor factor
     * @return int
     */
    public int adjustAlpha(int color, float factor) {
        int alpha = Math.round(Color.alpha(color) * factor);
        int red = Utils.red(color);
        int green = Utils.green(color);
        int blue = Utils.blue(color);
        return Color.argb(alpha, red, green, blue);
    }

    private int dp(int dp) {
        return (int) (dp * mDensity + DRAW_RADIUS_F);
    }

    public void setDrawRadius(final float radius) {
        mDrawRadius = radius;
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        int action = touchEvent.getAction();
        isPointMove = false;
        switch (action) {
            case TouchEvent.PRIMARY_POINT_DOWN: {
                if (!isHover) {
                    break;
                }
                isPressUp = false;
                isDrawFinish = false;
                mDownX = (int) touchEvent.getPointerPosition(touchEvent.getIndex()).getX();
                mDownY = (int) touchEvent.getPointerPosition(touchEvent.getIndex()).getY();
                updateDrawData();
                break;
            }
            case TouchEvent.POINT_MOVE:
                if (!isHover) {
                    break;
                }
                if (isDrawFinish) {
                    isDrawFinish = false;
                }
                isPointMove = true;
                int downX = (int) touchEvent.getPointerPosition(touchEvent.getIndex()).getX();
                int downY = (int) touchEvent.getPointerPosition(touchEvent.getIndex()).getY();
                if (!mRect.isInclude(downX + getLeft(), downY - mStatusBarHeight + getTop())) {
                    setDrawRadius(DRAW_RADIUS_ZERO);
                } else {
                    mDownX = downX;
                    mDownY = downY - mStatusBarHeight;
                    updateDrawData();
                    setDrawRadius(dp(DRAW_RADIUS_MOVE));
                }
                break;
            case TouchEvent.CANCEL:
            case TouchEvent.PRIMARY_POINT_UP:
                mStepRadius = (int) (5 * mStepRadius);
                isPressUp = true;
                if (isDrawFinish) {
                    isDrawFinish = false;
                    setDrawRadius(dp(DRAW_RADIUS_MOVE));
                }
                break;
        }
        return true;
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        if (isDrawFinish) {
            return;
        }
        canvas.drawColor(0x15000000, Canvas.PorterDuffMode.OVERLAY);
        if (mStepRadius == 0) {
            return;
        }
        if (!isPointMove) {
            mDrawRadius += mStepRadius;
        }

        if (mDrawRadius > dp(DRAW_RADIUS_MOVE) && !isPressUp) {
            setDrawRadius(DRAW_RADIUS_ZERO);
            isDrawFinish = true;
        }

        if (mDrawRadius > mMaxRadius) {
            setDrawRadius(DRAW_RADIUS_ZERO);
            canvas.drawCircle((float) mDownX, (float)mDownY, (float)mMaxRadius, mRevealPaint);
            isDrawFinish = true;
            return;
        }
        canvas.drawCircle((float)mDownX, (float)mDownY, (float)mDrawRadius, mRevealPaint);
    }
}
